package com.nado.extensions.reload;

import org.apache.commons.io.FileUtils;

import com.nado.extensions.util.ResourceLoader;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 파일이 변경되어 Reload해야 할 경우 사용하는 기본 {@link Reloadable} 구현체
 *
 * @author Web Platform Development Team
 * @version $Rev: 18373 $, $Date: 2008-07-18 11:50:49 +0900 (금, 18  7월 2008) $
 */
public abstract class AbstractFileReloadable implements Reloadable {
	/**
	 * 리로딩할 파일 목록.
	 */
	private Map<String, ReloadableFile> fileLists;

	/**
	 * Reload할 {@link ReloadableFile} 리스트를 생성한 후
	 * {@code addObjectWatcher}에 따라 ObjectWatcher에 등록할지 결정한다.
	 */
	public AbstractFileReloadable(boolean addObjectWatcher) {
		fileLists = new HashMap<String, ReloadableFile>();
		if (addObjectWatcher) {
			addObjectWatcher(this);
		}
	}

	/**
	 * 파일의 변경 사항을 확인한다.
	 */
	public boolean isChanged() {
		for (Map.Entry<String, ReloadableFile> entry : fileLists.entrySet()) {
			ReloadableFile fileInfo = entry.getValue();
			if (fileInfo.isClassPath()) {
				continue;
			}
			File currentFile = new File(entry.getKey());
			if (FileUtils.isFileNewer(currentFile, fileInfo.getLastModifed())) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Reload할 파일 목록을 반환한다.
	 */
	protected List<String> getReloadFiles() {
		List<String> reloadFiles = new ArrayList<String>(fileLists.size());
		for (ReloadableFile fileInfo : fileLists.values()) {
			reloadFiles.add(fileInfo.getName());
		}
		return reloadFiles;
	}

	/**
	 * {@link ReloadableFile}을 추가한다.
	 * 키 정보로는 {@link ReloadableFile#fullPath} fullPath 정보를 활용한다.
	 */
	protected void addReloadableFile(ReloadableFile reloadableFile) {
		fileLists.put(reloadableFile.getFullPath(), reloadableFile);
	}

	/**
	 * {@link ReloadableFile} 리스트를 반환한다.
	 */
	protected final Map<String, ReloadableFile> getReloadableFiles() {
		return fileLists;
	}

	/**
	 * {@link ReloadableFile} 리스트를 삭제한다.
	 */
	protected void clearReloadableFiles() {
		fileLists.clear();
	}

	/**
	 * {@link Reloadable} 객체를 {@link WatchedObject}에 등록한다.
	 */
	protected void addObjectWatcher(Reloadable reloadable) {
		WatchedObject.getInstance().add(reloadable);
	}

	/**
	 * 지정된 파일을 찾아 {@link InputStream}을 반환한다.
	 * non-classpath 구역에서 찾은 후 존재하지 않으면 claspath 구역에서 찾는다.
	 *
	 * @return InputStream
	 * @throws IOException 파일을 찾지 못한 경우 발생하는 Exception.
	 */
	protected InputStream loadFile(String filename, ResourceLoader resourceLoader) throws IOException {
		InputStream inputStream;

		File nonClassPathFile = new File(filename);
		if (nonClassPathFile.exists()) {
			inputStream = new FileInputStream(nonClassPathFile);
			addReloadableFile(getReloadableFile(
					filename, nonClassPathFile.lastModified(), false));
		} else {
			inputStream = resourceLoader.getResourceAsStream(filename);
			if (inputStream != null) {
				addReloadableFile(getReloadableFile(filename, 0, true));
			}
		}

		return inputStream;
	}

	/**
	 * 파일 정보를 통해 {@link ReloadableFile} 객체를 생성해서 반환한다.
	 */
	protected ReloadableFile getReloadableFile(
			String filename, long lastModified, boolean classPath) {
		ReloadableFile reloadableFile =
				new ReloadableFile(filename, lastModified, classPath);
		return reloadableFile;
	}
}